feature(pedm): SQL initialization#1302
Conversation
- `serve` arguments are changed - it now takes the `Config` struct instead of a path - pipe name is now part of `Config` rather than a direct argument - `Config` struct is now exported as `devolutions_pedm::Config` - it can be used by server implementations directly - `serve` is now exported as `devolutions_pedm::serve`
This commit makes it public again. It is used by the generate-openapi tool in the `devolutions_pedm::api::openapi` call.
- adds version tracking in the `pedm_schema_version` table
- adds table initialization
- error checking is strict and initialization only happens if the
version table is not found
- adds SQLite error handling for microsecond timestamp conversion to/from
Chrono
- fixes a bug in the libSQL schema where datetimes were stored
incorrectly
- they were being stored fractionally, instead of the full
microseconds since epoch
- adds `/about` endpoint to get basic info about the running application
- I used this to ensure that the libsql date handling is functional
A few other changes are included:
- libsql feature set is changed (default is now false, "stream" removed)
This adds changes added to PR #1301.
Let maintainers know that an action is required on their side
|
Desired changes were removed in the Cargo.toml reversion commit. They are restored now. A few `mod` statements are moved outside of the Windows-only block.
SQLite stores integers in 64 bit. If we try to get i32, it is just [cast internally](https://docs.rs/libsql/0.9.2/src/libsql/rows.rs.html#169-177) as i32. The logic for fallible int conversion is already written. This commit makes use of it and makes it explicit that we are actually retrieving an i64 from the database before converting it to our model type.
There was a problem hiding this comment.
question: Is there some rationale for when the pedm prefix is used in table names?
There was a problem hiding this comment.
It was in case the database is also used by Gateway or other programs in the future.
There was a problem hiding this comment.
I see what you mean. The table naming is inconsistent. And I can't see other programs using this specific http_request table.
Tables are renamed in 72bae7b. The database is meant for isolated PEDM usage. This is expressed in the example config, with a database name of pedm or file of pedm.sqlite.
There was a problem hiding this comment.
I think it’s better to use a different database completely! 🙂
I agree with the way you renamed the tables!
`pedm_` prefix is removed. The usage was inconsistent. `pedm_schema_version` is now simply `version`. The database is meant for isolated PEDM usage. This is expressed in the example config, with a database name of `pedm` or file of `pedm.sqlite`.
- use `DateTime` `Display` instead of `Debug` in `ParseTimestampError` `Display` - comments should go above `#[cfg(feature)]` - adds random comments
Postgres `log_http_layer` was calling `query_one` when nothing was returned. The database was still written but the function returned an error.
This one was a bit funny. `query_opt` returns `Option<Row>`. It was doing `opt_row.map(|r| r.get::<_, Option<D>(0))`, which would be `Some(Some(D))` or `None` if the row was not found. With `unwrap_or_default`, this was returning `Some(D)` or `None`, which is perfectly well. However, the actual column is not nullable so `opt_row.map(|r| r.get::<_, D>(0))` is simpler.
This disambiguates `requests_received` and `request_count`.
| pub(crate) request_count: i32, | ||
| /// The request count at the time of the server startup. | ||
| pub(crate) startup_request_count: i32, |
There was a problem hiding this comment.
naming: I feel it’s not necessary to add the startup prefix, since this is a field of a struct named StartupInfo. Or maybe I don’t really understand what this is? Is the intention to count the total of requests ever made? "All time request count"?
There was a problem hiding this comment.
It was because I was using the field in #[serde(flatten)]. But evidently the naming still wasn't clear.
My proposal is c17bdc5.
|
|
||
| mod err; | ||
|
|
||
| use chrono::{DateTime, Utc}; |
There was a problem hiding this comment.
style: Inconsistent import order.
There was a problem hiding this comment.
| /// Returns the schema version from the `version` table. | ||
| async fn get_schema_version(&self) -> Result<i16, DbError>; | ||
|
|
||
| /// Initializes the database schema. | ||
| /// | ||
| /// This creates tables. | ||
| async fn init_schema(&self) -> Result<(), DbError>; |
There was a problem hiding this comment.
question: Should we really expose the schema version to the user of the Database trait?
I think it’s more appropriate to define a single method called setup that is performing any backend-dependent setup sequence, including initializing the schema and/or performing migrations if necessary.
See how it’s done in job-queue/job-queue-libsql crates:
devolutions-gateway/crates/job-queue-libsql/src/lib.rs
Lines 174 to 178 in cdbe0b7
There was a problem hiding this comment.
My proposal is 4eed892.
I put shared logic in the Db wrapper setup function, formerly called init_schema.
I kept get_schema_version and init_schema on the Database trait because they are just DB operations.
I didn't do Database::setup because I didn't want to duplicate the code in Db::setup. But I can make Database::setup with the duplication if you prefer to keep the logic in the trait implementation.
There was a problem hiding this comment.
Fair enough. My thinking was to keep the exact setup logic as an implementation detail to leave more leeway, but at this point it does not really matter so we may as well avoid the logic duplication as you suggest. Certainly a better abstraction will emerge when it becomes necessary.
Benoît Cortier (CBenoit)
left a comment
There was a problem hiding this comment.
thought: It seems the /about route is close to something that is typically called /health. I’m curious if you have further motivations for the /about route?
| seconds integer NOT NULL | ||
| ); | ||
|
|
||
| INSERT INTO version (version) VALUES (1) ON CONFLICT DO NOTHING; No newline at end of file |
There was a problem hiding this comment.
question: Any objection to starting with 0? I think it can be useful in the future, so we can index an array of migrations.
There was a problem hiding this comment.
I just chose 1 because SQL sequences start at 1.
Let me know what you prefer and I'll change it.
There was a problem hiding this comment.
I think 0 would be better because we wouldn’t index into a SQL sequence. But, it’s not a hill I’m willing to die on either as it’s not too hard to throw a - 1 in there.
This further disambiguates `startup_request_count` and `current_request_count` in `AboutData`.
I went through every file in the crate.
This adds a placeholder for backend-specific setup code, such as pragma tuning on SQLite.
As for the motivation, I included some info that was useful for debugging. |
Benoît Cortier (CBenoit)
left a comment
There was a problem hiding this comment.
LGTM! Feel free to merge!
pedm_schema_versiontableChrono
/aboutendpoint to get basic info about the running applicationA few other changes are included:
This PR includes all commits from PR #1301, so an option is to merge this one only instead of #1301 first.